﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace App.Utility.Paging
{
    /// <summary>
    /// Defines a sort expression
    /// </summary>
    public class Sort
    {
        [MessageBodyMember]
        public string Parameter { get; set; }
        [MessageBodyMember]
        public string TypeName { get; set; }
        [MessageBodyMember]
        public bool Ascending { get; set; }
    }

    /// <summary>
    /// Defines a filter expression
    /// </summary>
    public class Filter
    {
        [MessageBodyMember]
        public string Parameter { get; set; }
        [MessageBodyMember]
        public string TypeName { get; set; }
        [MessageBodyMember]
        public object Value { get; set; }
        [MessageBodyMember]
        public FilterBinaryExpression BinaryExpression { get; set; }
    }

    [CollectionDataContract]
    public class ContractList<T> : List<T>
    {
        public ContractList() { }
        public ContractList(IEnumerable<T> values) : base(values) { }
    }

    /// <summary>
    /// Written By: Charles Cook
    /// 
    /// Defines a page request with support for Filtering and Sorting
    /// </summary>
    [MessageContract]
    public class PageRequest
    {
        [MessageBodyMember]
        public ContractList<Sort> Sorts { get; set; }

        [MessageBodyMember]
        public ContractList<Filter> Filters { get; set; }

        [MessageBodyMember]
        public int Index { get; set; }

        [MessageBodyMember]
        public int PageSize { get; set; }

        [MessageBodyMember]
        [DefaultValue(FilterTypes.And)]
        public FilterTypes FilterType { get; set; }

        public enum FilterTypes { And, Or };

        /// <summary>
        /// Create a PageRequest for the first page with a page size
        /// of ten and no sorting or filtering.
        /// </summary>
        public PageRequest()
        {
            Sorts = new ContractList<Sort>();
            Filters = new ContractList<Filter>();
            Index = 0;
            PageSize = 10;
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a page size
        /// of ten and no sorting or filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        public PageRequest(int Index)
        {
            Sorts = new ContractList<Sort>();
            Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = 10;

            CheckRanges();
        }


        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size and no sorting or filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        public PageRequest(int Index, int PageSize)
        {
            Sorts = new ContractList<Sort>();
            Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with sorting but no filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="SortBy">Parameter to sort on</param>
        /// <param name="Ascending">Direction for the sort</param>
        public PageRequest(int Index, int PageSize,
            string SortBy, Type SortType, bool Ascending)
        {
            Sorts = new ContractList<Sort>();
            Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();

            Sorts.Add(new Sort
            {
                Ascending = Ascending,
                TypeName = SortType.Name,
                Parameter = SortBy
            });
        }

        private void CheckRanges()
        {

            if (this.Index < 0)
                this.Index = 0;
            if (this.PageSize < 1)
                this.PageSize = 1;
        }



        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with sorting but no filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="Sorts">List of sorts to use</param>
        public PageRequest(int Index, int PageSize,
            ContractList<Sort> Sorts)
        {
            this.Sorts = Sorts;
            this.Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with out sorting but with filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="FilterBy">The parameter to filter upon</param>
        /// <param name="FilterType">The type of the filter value</param>
        /// <param name="Value">The value to filter to</param>
        /// <param name="Equal">Filter on equal or not equal</param>
        public PageRequest(int Index, int PageSize,
            string FilterBy, Type FilterType, object Value, FilterBinaryExpression binaryExpression)
        {
            this.Sorts = new ContractList<Sort>();
            this.Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();

            Filters.Add(new Filter
            {
                BinaryExpression = binaryExpression,
                Parameter = FilterBy,
                Value = Value,
                TypeName = FilterType.Name
            });
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with sorting and filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="FilterBy">The parameter to filter upon</param>
        /// <param name="FilterType">The type of the filter value</param>
        /// <param name="Value">The value to filter to</param>
        /// <param name="Equal">Filter on equal or not equal</param>
        /// <param name="SortBy">Parameter to sort on</param>
        /// <param name="SortType">The type of the parameter being sorted</param>
        /// <param name="Ascending">Direction for the sort</param>
        public PageRequest(int Index, int PageSize,
            string FilterBy, Type FilterType, object Value, FilterBinaryExpression binaryExpression,
            string SortBy, Type SortType, bool Ascending)
        {
            this.Sorts = new ContractList<Sort>();
            this.Filters = new ContractList<Filter>();
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();

            Filters.Add(new Filter
            {
                BinaryExpression = binaryExpression,
                Parameter = FilterBy,
                Value = Value,
                TypeName = FilterType.Name
            });

            Sorts.Add(new Sort
            {
                Ascending = Ascending,
                TypeName = SortType.Name,
                Parameter = SortBy
            });
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with sorting and filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="Filters">List of filters to use</param>
        /// <param name="Sorts">List of sorts to use</param>
        public PageRequest(int Index, int PageSize,
            ContractList<Filter> Filters, ContractList<Sort> Sorts)
        {
            this.Sorts = Sorts;
            this.Filters = Filters;
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();
        }

        /// <summary>
        /// Create a PageRequest for the specified page with a given 
        /// page size with filtering.
        /// </summary>
        /// <param name="Index">Page to retrieve (zero based)</param>
        /// <param name="PageSize">The size of the pages to use</param>
        /// <param name="Filters">List of filters to use</param>
        public PageRequest(int Index, int PageSize,
            ContractList<Filter> Filters)
        {
            this.Sorts = new ContractList<Sort>();
            this.Filters = Filters;
            this.Index = Index;
            this.PageSize = PageSize;

            CheckRanges();
        }
    }
}
